﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Ratatoskr
{
    public class ServerComponent<T> : ServerComponent where T : class
    {

        #region Construction & Client Legitimization

        public ServerComponent(string type, bool open = true, bool single = false)
            : base(type, open, single)
        {
        }

        protected override bool LegitimizeClient(Component c)
        {
            return (c as T != null && ExtendLegitimizeClient(c));
        }

        #endregion

    }

    public class ServerComponent : Component
    {

        #region Fields & Trigger Event

        protected Dictionary<Component, LinkProfile> _clients = new Dictionary<Component, LinkProfile>();

        protected bool _rebuild;

        protected event EventHandler<ServerComponentEventArgs> _trigger;

        protected bool _open;
        public bool OpenTrigger
        {
            get { return _open; }
        }

        protected bool _single = false;
        public bool SingleSubscriber
        {
            get { return _single; }
        }

        #endregion

        #region Construction, Trigger Methods & Helpers

        public ServerComponent(string type, bool open = true, bool single = false)
            : base(type)
        {
            _open = open;
            _single = single;
        }

        public bool Trigger(object[] args = null)
        {
            if (_open)
                return DoTrigger(args);
            return false;
        }

        protected bool DoTrigger(object[] args = null)
        {
            EventHandler<ServerComponentEventArgs> trigger = _trigger;
            if (trigger == null || !ExtendTriggerConditions())
                return false;
            if (_rebuild)
            {
                BuildLinks();
                _rebuild = false;
            }
            _trigger(this, ComposeArgs(args));
            return true;
        }

        protected void BuildLinks()
        {
            IOrderedEnumerable<KeyValuePair<Component, LinkProfile>>
                clients = _clients.OrderBy(client => client.Value.priority);
            _clients.Clear();
            if (_single && clients.Count() > 0)
                _trigger = clients.Last().Value.handler;
            else _trigger = null;
            foreach (KeyValuePair<Component, LinkProfile> client in clients)
            {
                _clients[client.Key] = client.Value;
                if (!_single)
                    _trigger += client.Value.handler;
            }
        }

        /// <summary>
        /// Compose an array of objects containing any information needed to
        /// perform the triggered event, e.g. GameTime, SpriteBatch, etc.
        /// All ServerComponents with a given Type property should pass arguments
        /// with the same signature.
        /// </summary>
        /// <returns>Arguments to be sent with the trigger event.</returns>
        protected virtual ServerComponentEventArgs ComposeArgs(object[] args = null)
        {
            return new ServerComponentEventArgs(args);
        }

        #endregion

        #region Client Management Helper Methods

        protected internal bool Register(Component c, LinkProfile l)
        {
            if (l.handler == null)
                throw new ArgumentNullException("Attempted to register a null trigger handler.");
            try
            {
                if (!_clients.ContainsKey(c))
                {
                    if (LegitimizeClient(c))
                    {
                        _clients[c] = l;
                        _rebuild = true;
                        return true;
                    }
                }
            }
            catch (ArgumentNullException e) { throw new ArgumentNullException("Attempted to register a null client component.", e); }
            return false;
        }

        protected internal void Sever(Component c)
        {
            try
            {
                _clients.Remove(c);
                _rebuild = true;
            }
            catch (ArgumentNullException e) { throw new ArgumentNullException("Attempted to sever a null client component.", e); }
        }

        protected virtual bool LegitimizeClient(Component c)
        {
            return (c as Component != null && ExtendLegitimizeClient(c));
        }

        #endregion

        #region Virtual Methods & Implementation Notes

        /// <summary>
        /// Override this if client components need to satisfy some condition, e.g. implement an interface.
        /// </summary>
        /// <param name="c">The client component to legitimize for this server.</param>
        /// <returns>True if client meets conditions, false otherwise.</returns>
        protected virtual bool ExtendLegitimizeClient(Component c)
        {
            return true;
        }

        protected virtual bool ExtendTriggerConditions()
        {
            return true;
        }

        #endregion

    }

    public class ServerComponentEventArgs : EventArgs
    {
        private object[] _args;
        public object[] Args { get { return _args; } }
        public ServerComponentEventArgs(object[] args) { _args = args; }
    }
}